package com.hsgene.authority.service.impl;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.hsgene.authority.constants.AccountPermissionCondition;
import com.hsgene.authority.domain.GeneticTestingOperationAccountPermission;
import com.hsgene.authority.dto.OperationAccountPermissionDto;
import com.hsgene.authority.dto.OperationPermissionTree;
import com.hsgene.authority.exception.AuthorityErrorCode;
import com.hsgene.authority.persistence.PermissionRepository;
import com.hsgene.authority.service.OperationAccountPermissionService;
import com.hsgene.authority.service.PermissionService;
import com.hsgene.authority.service.api.OperationOrganizationService;
import com.hsgene.common.DeleteFlagType;
import com.hsgene.common.util.basic.JsonUtils;
import com.hsgene.common.util.basic.UUID32;
import com.hsgene.common.util.collection.SetUtils;
import com.hsgene.common.util.exception.IllegalParamException;
import com.hsgene.common.util.result.ApiResult;
import com.hsgene.common.util.result.NullObject;
import com.hsgene.common.util.support.LimitHelper;
import com.hsgene.common.util.support.Pagination;
import com.hsgene.common.util.validation.FieldChecker;
import com.hsgene.user.dto.orginazation.DepartmentDto;
import com.hsgene.user.dto.orginazation.OrganizationDto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.stream.Collectors;

/**
 * 运营管理权限ServiceImpl
 *
 * @author wangbing
 * @version 1.0, 2018/11/6
 */
@Service
public class OperationAccountPermissionServiceImpl implements OperationAccountPermissionService {

    private final static Logger LOGGER = LoggerFactory.getLogger(OperationAccountPermissionServiceImpl.class);

    @Resource
    private PermissionRepository permissionRepository;

    @Resource
    private PermissionService permissionService;

    @Resource
    private OperationOrganizationService operationOrganizationService;

    @Resource
    private RedisTemplate<Serializable, Serializable> redisTemplate;

    @Value("${redis.prefix.permission.department}")
    private String redisPermissionDepartmentKey;

    /**
     * 刷新所有部门权限
     */
    @Override
    public void refreshAllDepartmentPermission() {
        ApiResult<Pagination<DepartmentDto>> result = operationOrganizationService.queryDepartment(1, Integer.MAX_VALUE, null, null);
        if (!result.isSucc()) {
            LOGGER.error("获取部门信息失败，无法初始化部门权限！原因：" + result.getDesc());
            return;
        }
        if (!CollectionUtils.isEmpty(result.getData().getResult())) {
            result.getData().getResult().forEach(e -> refreshDepartmentPermission(e.getId()));
        }
    }

    /**
     * 刷新指定部门权限
     *
     * @param departmentId 部门id
     */
    @Override
    public void refreshDepartmentPermission(String departmentId) {
        ApiResult<OperationPermissionTree> result = findDepartmentPermission(departmentId, false, true);
        if (!result.isSucc()) {
            LOGGER.error("无法初始化部门[{}]的权限信息！原因：{}", departmentId, result.getDesc());
            return;
        }
        Set<String> urls = extractPermissionApi(result.getData(), Sets.newHashSet());
        String redisKey = redisPermissionDepartmentKey + departmentId;
        String[] urlArray = urls.toArray(new String[]{});
        redisTemplate.delete(redisKey);
        if (urlArray.length > 0) {
            redisTemplate.opsForList().leftPushAll(redisKey, urlArray);
        }
    }

    /**
     * 提取权限树API
     *
     * @param tree 权限树
     * @param urls API集合
     * @return API集合
     */
    private Set<String> extractPermissionApi(OperationPermissionTree tree, Set<String> urls) {
        if (tree == null || CollectionUtils.isEmpty(tree.getChildrenPermissions())) {
            return urls;
        }
        tree.getChildrenPermissions().forEach(e -> {
            if (e.getSelected() && !StringUtils.isEmpty(e.getUrl()) && !StringUtils.isEmpty(e.getRequestMethod())) {
                String method = e.getRequestMethod();
                String uri = e.getUrl();
                urls.add(method + " " + uri);
            }
            if (e.getSelected() && !CollectionUtils.isEmpty(e.getChildrenPermissions())) {
                urls.addAll(extractPermissionApi(e, Sets.newHashSet()));
            }
        });
        return urls;
    }

    @Override
    public ApiResult<OperationPermissionTree> findDepartmentPermission(String departmentId, boolean trimCommon, boolean trimNotSelected) {
        FieldChecker.checkEmpty(departmentId, "部门id");
        ApiResult<OrganizationDto> departmentResult = operationOrganizationService.findOrganization(departmentId, "department");
        if (!departmentResult.isSucc()) {
            LOGGER.error("查询部门信息失败：" + departmentResult.getDesc());
            return ApiResult.fail(departmentResult.getError());
        }
        OrganizationDto departmentInfo = departmentResult.getData();
        OperationPermissionTree tree = permissionService.getWholePermissionTrees();
        if (tree == null) {
            return ApiResult.fail(AuthorityErrorCode.PERMISSION_TREE_NOT_EXISTED);
        }
        List<GeneticTestingOperationAccountPermission> departmentPermissions = permissionRepository.getDepartmentPermission(departmentId, departmentInfo.getCompanyId());
        if (!CollectionUtils.isEmpty(departmentPermissions)) {
            Set<String> selectedIds = departmentPermissions.stream()
                    .map(GeneticTestingOperationAccountPermission::getPermissionId)
                    .collect(Collectors.toSet());
            fillSelectedPermissions(selectedIds, tree.getChildrenPermissions());
        }
        trimTree(tree, trimCommon, trimNotSelected);

        return ApiResult.succ(tree);
    }

    /**
     * 填充选中的权限信息
     *
     * @param selectedIds 选中的权限id列表
     * @param trees       完整权限树
     */
    private void fillSelectedPermissions(Set<String> selectedIds, List<OperationPermissionTree> trees) {
        trees.parallelStream().forEach(e -> {
            if (selectedIds.contains(e.getId())) {
                e.setSelected(Boolean.TRUE);
            }
            if (!CollectionUtils.isEmpty(e.getChildrenPermissions())) {
                fillSelectedPermissions(selectedIds, e.getChildrenPermissions());
            }
        });
    }

    @Override
    public ApiResult<OperationPermissionTree> getSystemSuperPermission() {
        OperationPermissionTree tree = permissionService.getWholePermissionTrees();
        if (tree == null) {
            return ApiResult.fail(AuthorityErrorCode.PERMISSION_TREE_NOT_EXISTED);
        }
        fillSystemSuperPermissions(tree);
        trimTree(tree, true, true);

        return ApiResult.succ(tree);
    }

    /**
     * 修剪权限树
     *
     * @param tree            权限树信息
     * @param trimCommon      是否修剪通用权限
     * @param trimNotSelected 是否修剪未选中的权限
     */
    private void trimTree(OperationPermissionTree tree, boolean trimCommon, boolean trimNotSelected) {
        boolean needTrim = tree != null && (trimCommon || trimNotSelected);
        if (needTrim) {
            List<OperationPermissionTree> children = tree.getChildrenPermissions();
            if (!CollectionUtils.isEmpty(children)) {
                List<OperationPermissionTree> copyChildren = Lists.newArrayList();
                for (OperationPermissionTree child : tree.getChildrenPermissions()) {
                    boolean trimmed = false;
                    if (trimCommon && isCommonPermission(child)) {
                        trimmed = true;
                    }
                    if (!trimmed && trimNotSelected && !child.getSelected()) {
                        trimmed = true;
                    }
                    if (!trimmed) {
                        trimTree(child, trimCommon, trimNotSelected);
                    }
                    if (!trimmed) {
                        copyChildren.add(child);
                    }
                }
                tree.setChildrenPermissions(copyChildren);
            }
        }
    }

    private boolean isCommonPermission(OperationPermissionTree tree) {
        return tree.getCommonFlag() == 1;
    }

    /**
     * 填充超管选中的权限信息
     *
     * @param tree 完整权限树
     */
    private void fillSystemSuperPermissions(OperationPermissionTree tree) {
        if (tree != null) {
            tree.setSelected(Boolean.TRUE);
            if (!CollectionUtils.isEmpty(tree.getChildrenPermissions())) {
                tree.getChildrenPermissions().parallelStream().forEach(this::fillSystemSuperPermissions);
            }
        }
    }

    private void fillSelectedTree(OperationPermissionTree tree, List<String> ids) {
        if (!CollectionUtils.isEmpty(tree.getChildrenPermissions())) {
            tree.getChildrenPermissions().forEach(child -> fillSelectedTree(child, ids));
        }
        if (!"root".equals(tree.getId()) && ids.contains(tree.getId())) {
            tree.setSelected(Boolean.TRUE);
        }
    }

    private void fillParentSelected(OperationPermissionTree tree) {
        Stack<OperationPermissionTree> stack = new Stack<>();
        stack.push(tree);
        fillParentSelected(tree, stack);
    }

    private void fillParentSelected(OperationPermissionTree tree, Stack<OperationPermissionTree> stack) {
        if (!"root".equals(tree.getId()) && tree.getSelected()) {
            stack.peek().setSelected(Boolean.TRUE);
        } else {
            stack.push(tree);
            tree.getChildrenPermissions().stream()
                    .filter(e -> e.getCommonFlag() == 0)
                    .forEach(e -> fillParentSelected(e, stack));
            stack.pop();
            if (tree.getSelected()) {
                stack.peek().setSelected(Boolean.TRUE);
            }
        }
    }

    private List<String> extractSelectedId(OperationPermissionTree tree, List<String> ids) {
        if (tree.getSelected() && !"root".equals(tree.getId()) && tree.getCommonFlag() == 0) {
            LOGGER.debug("选中的权限：" + tree.getPermissionName());
            ids.add(tree.getId());
        }
        if (!CollectionUtils.isEmpty(tree.getChildrenPermissions())) {
            tree.getChildrenPermissions().forEach(e -> extractSelectedId(e, ids));
        }
        return ids;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApiResult<NullObject> updateDepartmentPermission(String departmentId, List<String> ids) {
        FieldChecker.checkEmpty(departmentId, "部门id");
        ApiResult<OrganizationDto> departmentResult = operationOrganizationService.findOrganization(departmentId, "department");
        if (!departmentResult.isSucc()) {
            LOGGER.error("查询部门信息失败：" + departmentResult.getDesc());
            return ApiResult.fail(departmentResult.getError());
        }
        OperationPermissionTree tree = permissionService.getWholePermissionTrees();
        LOGGER.debug("WholeTree:" + JsonUtils.objectToJsonIgnoreNull(tree));
        fillSelectedTree(tree, ids);
        LOGGER.debug("SelectedTree:" + JsonUtils.objectToJsonIgnoreNull(tree));
        fillParentSelected(tree);
        LOGGER.debug("ParentSelectedTree:" + JsonUtils.objectToJsonIgnoreNull(tree));
        List<String> selectedIds = extractSelectedId(tree, Lists.newArrayList());
        LOGGER.debug("SelectedIds:" + JsonUtils.objectToJson(selectedIds));
        OrganizationDto departmentInfo = departmentResult.getData();
        List<GeneticTestingOperationAccountPermission> departmentPermissions = permissionRepository.getDepartmentPermission(departmentId, departmentInfo.getCompanyId());
        Date now = new Date();
        Set<String> oldSelected = departmentPermissions.parallelStream().map(e -> {
            if (!selectedIds.contains(e.getPositionId())) {
                e.setDeleteFlag(DeleteFlagType.DELETE.getCode());
                e.setUpdateDateTime(now);
            }
            return e.getPositionId();
        }).collect(Collectors.toSet());
        Set<String> newSelected = Sets.newHashSet(selectedIds);
        Set<String> newAdd = SetUtils.diff(newSelected, oldSelected);
        if (!CollectionUtils.isEmpty(newAdd)) {
            List<GeneticTestingOperationAccountPermission> newPermissions = newAdd.stream()
                    .map(e -> {
                        GeneticTestingOperationAccountPermission permission = new GeneticTestingOperationAccountPermission();
                        permission.setId(UUID32.randomUUIDString());
                        permission.setDepartmentId(departmentId);
                        permission.setPermissionId(e);
                        permission.setCreateDateTime(now);
                        permission.setDeleteFlag(DeleteFlagType.NOT_DELETED.getCode());
                        return permission;
                    }).collect(Collectors.toList());
            departmentPermissions.addAll(newPermissions);
        }
        if (!CollectionUtils.isEmpty(departmentPermissions)) {
            permissionRepository.saveAllAccountPermissions(departmentPermissions);
        }
        refreshDepartmentPermission(departmentId);

        return ApiResult.succ();
    }

    @Override
    public ApiResult<NullObject> initDepartmentPermission(String departmentId, String initType) {
        if ("init".equalsIgnoreCase(initType)) {
            refreshDepartmentPermission(departmentId);
        } else if ("clear".equalsIgnoreCase(initType)) {
            permissionRepository.clearAllAccountPermissions(departmentId);
            redisTemplate.delete(redisPermissionDepartmentKey + departmentId);
        } else {
            LOGGER.error("非法的参数值initType:{}", initType);
            throw new IllegalParamException("非法的参数值initType:" + initType);
        }
        return ApiResult.succ();

    }

    @Override
    public ApiResult<Pagination<OperationAccountPermissionDto>> queryPermissions(AccountPermissionCondition condition, LimitHelper limitHelper) {
        FieldChecker.checkEmpty(limitHelper, "分页参数");

        int totalCount = permissionRepository.queryAccountPermissionCount(condition);
        Pagination<OperationAccountPermissionDto> pagination = Pagination.newInstance(limitHelper, totalCount);
        if (totalCount > 0 && limitHelper.getOffset() <= totalCount) {
            List<OperationAccountPermissionDto> permissions = permissionRepository.queryAccountPermissions(condition, limitHelper);
            int number = limitHelper.getOffset() + 1;
            for (OperationAccountPermissionDto permission : permissions) {
                permission.setNumber(number++);
            }
            pagination.setResult(permissions);
        }

        return ApiResult.succ(pagination);
    }

    @Override
    public ApiResult<OperationPermissionTree> findPermission(String userId, String permissionId, boolean containParent, boolean containChildren) {
        return null;
    }
}
